/** @file    TaskManagerImpl.cpp
 *  @author  Alessandro Polo
 *  @version $Id: TaskManagerImpl.cpp 2051 2010-03-07 08:27:48Z alex $
 *  @brief
 * File containing methods for the TaskManagerImpl class.
 * The header for this class can be found in TaskManagerImpl.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "TaskManagerImpl.h"


 #include <core/ThreadMethod.h>


using namespace wosh;


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

TaskManagerImpl::TaskManagerImpl( BundleGeneric& bundle ) : BundleGenericWorker(bundle)
 {
	this->clock.setOwnerObject(&bundle);
	this->clock.setListener(this);

	this->workersPool = new ThreadPool<ScheduledTaskExecuter>();
	this->workersPool->setWorkersCount<TaskManagerImpl>(3, this);
	this->workersPool->setOwnerObject(&bundle);

 }


TaskManagerImpl::~TaskManagerImpl()
 {
	if ( isThreadRunning() ) {
		quitThread();
		Thread::waitThread( 10000 );
	 }

	this->workersPool->setWorkersCount<TaskManagerImpl>(0, this);
	delete this->workersPool; this->workersPool = NULL;

	Log(LOG_VERBOSE, ":~TaskManagerImpl() Destroyed." );

 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD

bool TaskManagerImpl::initializingThread()
 {
	this->workersPool->waitApplyState(Thread::STATE_RUNNING, 30000);

	clock.startMetronome(true);

	return true;
 }

void TaskManagerImpl::runThread()
 {
	Log(LOG_VERBOSE, ":runThread(): STARTING" );

	while(this->running) {
		this->queueMux.lock();
		if ( this->queueTask.empty() ) {
			this->queueMux.unLock();
			Thread::sleepForMSec(100);
			setThreadAlive();
			continue;
		 }
		// priority ordered FIFO
		ScheduledTask* task = this->queueTask.top();
		this->queueTask.pop();
		this->queueMux.unLock();
		setThreadAlive();


		this->workersPool->getThreads().transactionBeginRead();
		ScheduledTaskExecuter* tW = this->workersPool->waitNextIdle_(10000);
		if ( tW == NULL ) {
			Log(LOG_WARNING, " Threads seems Locked [after 10sec timeout]!" );
			tW = this->workersPool->waitNextIdle_(-1);
		 }
		tW->setTask(task);
		this->workersPool->getThreads().transactionEnd();
		setThreadAlive();
	 }

	Log(LOG_VERBOSE, ":runThread(): EXITING" );
 }

void TaskManagerImpl::exitingThread()
 {
	clock.stopMetronome();

 }

////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// PROCESSING

void TaskManagerImpl::scheduleTask( ScheduledTask* newTask )
 {
	// priority ordered FIFO
	this->queueMux.lock();
	this->queueTask.push( newTask );
	this->queueMux.unLock();
 }

void TaskManagerImpl::evalEvent( int64 time, const Message* message )
 {
	ScheduledTask* newTask = NULL;
	this->automations.transactionBeginRead();
	tAutomationList::ConstIterator it;
	const tAutomationList::ConstIterator it_end = this->automations.end();
	for ( it=this->automations.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !(*it)->isEnabled() ) continue;
		if ( !(*it)->isTrigger( message, time ) ) continue;

		if ( newTask == NULL )
			newTask = new ScheduledTask( message->clone(), time );
		newTask->addTask( *it );
	 }
	this->automations.transactionEnd();
	if ( newTask == NULL )
		return;
	scheduleTask(newTask);
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ScheduledTaskExecuter::crashDetected( double probability ) {
	this->parent->Log(LOG_CRITICAL, ":crashDetected(%f)", probability );
 }


void ScheduledTaskExecuter::work() {
	this->processingLock.lockForWrite();
	if ( this->task == NULL ) {
		this->processingLock.unLock();
		Thread::sleepForMSec(50);
		return;
	 }

	std::vector<Automation*>::iterator it;
	const std::vector<Automation*>::iterator it_end = this->task->tasks.end();
	for ( it=this->task->tasks.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		(*it)->execute( task->message, task->timeTick );
	 }

	// finally delete the instance
	delete this->task; this->task = NULL;
	this->processing = false;
	this->processingLock.unLock();
 }